package com.softserveinc.moviedbapp.manager;

import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.AsyncTask;
import android.util.Log;

import com.softserveinc.moviedbapp.application.Settings;
import com.softserveinc.moviedbapp.cache.tables.MovieDetailsTable.MovieDetailsColumns;
import com.softserveinc.moviedbapp.cache.tables.MovieImagesTable.MovieImageColumns;
import com.softserveinc.moviedbapp.cache.tables.MovieTable.MovieColumns;
import com.softserveinc.moviedbapp.fragment.IFragmentCallback;
import com.softserveinc.moviedbapp.fragment.IMovieInfoCallback;
import com.softserveinc.moviedbapp.model.Movie;
import com.softserveinc.moviedbapp.model.MovieInfo;
import com.softserveinc.moviedbapp.provider.MovieDetailsProvider;
import com.softserveinc.moviedbapp.provider.MovieListProvider;
import com.softserveinc.moviedbapp.util.MovieAppConstants;

import java.util.ArrayList;
import java.util.List;

public class DataManager {

	private Settings mSettings;

	private boolean mIsConnected;

	private IFragmentCallback mMovieItemsFragment;
	private IMovieInfoCallback mMovieInfoFragment;
	private List<Movie> mMovies;
	private List<Movie> mMoviesSearchList;
	private MovieInfo mMovieInfo;
	private Context mContext;
	private String mLastSearchQuery;

	private List<Movie> mWidgetMoviesByRelease;
	private List<Movie> mWidgetMoviesByRating;
	private List<Movie> mWidgetMoviesByTitle;
	private BitMapManager mBitMapManager;

	private static final String TAG = DataManager.class.getSimpleName();

	private MovieInfoRetrieverAsync mMovieInfoRetrieverAsync;
	private MovieInfoScreensRetrieverAsync mMovieInfoScreensRetrieverAsync;

	private MoviePosterRetrieverAsync mMoviePosterRetrieverAsync;

	private MovieListRetrieverAsync mMovieListRetrieverAsync;

	public DataManager(final Context ctx, final Settings settings, final BitMapManager bmm) {
		mContext = ctx;
		mSettings = settings;
		mBitMapManager = bmm;
	}

	public void provideMovieList(final String searchQuery) {
		if (searchQuery == null) {
			/* no search, just show movie list */
			if (mMovies == null || mMovies.isEmpty()) {
				Log.d(TAG, "Going for the movie list, no instance in memory");
				mMovieListRetrieverAsync = new MovieListRetrieverAsync();
				mMovieListRetrieverAsync.execute();
			} else {
				Log.d(TAG, "Returning saved instance of movie list from memory");
				mMovieItemsFragment.onGettingMovieList();
				mMovieItemsFragment.onMovieListRetrieved(mMovies);
			}
		} else {
			/* search for a title */
			if (mLastSearchQuery == null || !mLastSearchQuery.equals(searchQuery)) {
				Log.d(TAG, "inside manageMovieList() GOING INTO SEARCH");
				mLastSearchQuery = searchQuery;
				new SearchMovieListAsync().execute(searchQuery);
			} else {
				Log.d(TAG, "inside manageMovieList() RETURNING SAVED SEARCH INSTANCE");
				mMovieItemsFragment.onGettingMovieList();
				mMovieItemsFragment.onMovieListRetrieved(mMoviesSearchList);
			}
		}
	}

	public void provideMoviePosters(final List<String> posterUrls) {
		Log.d(TAG, "gonna get movie posters");
		mMoviePosterRetrieverAsync = new MoviePosterRetrieverAsync();
		ListWrapper listWrapper = new ListWrapper();
		listWrapper.setPosterUrls(posterUrls);
		mMoviePosterRetrieverAsync.execute(listWrapper);

	}

	public void provideRefreshedMovieList() {
		new RefreshMovieListAsync().execute();
	}

	public void provideMovieInfo(final int movieId) {

		if (mMovieInfo == null || mMovieInfo.getMovieId() != movieId) {
			Log.d(TAG, "Going for the movie info, no instance in memory movieId = " + movieId);
			mMovieInfoRetrieverAsync = new MovieInfoRetrieverAsync();
			mMovieInfoRetrieverAsync.execute(movieId);
		} else {
			Log.d(TAG, "Returning saved instance of movie info from memory");
			mMovieInfoFragment.onGettingMovieInfo();
			mMovieInfoFragment.onMovieInfoRetrieved(mMovieInfo);
			Log.d(TAG, "mMovieInfo urls" + mMovieInfo.getBackdropImagesUrlsList());
		}

	}

	/* launch another async tast and download images from web */
	public void provideNextScreenForMovieInfo(final MovieInfo movieInfo) {

		Log.d(TAG, "GONNA GET SCREENS FOR MOVIE INFO");
		mMovieInfoScreensRetrieverAsync = new MovieInfoScreensRetrieverAsync();
		mMovieInfoScreensRetrieverAsync.execute(movieInfo);

	}

	/* gets a movie list ordered by release, rating or title */
	public void provideMovieListForWidget(final String orderBy, final String results) {

		Log.d(TAG, "Going to get a movie list for WIDGET");
		/* get user settings */
		String language = mContext.getResources().getConfiguration().locale.getLanguage();
		String parserMode = mSettings.getParserMode();

		Cursor cursor = mContext.getContentResolver().query(MovieListProvider.CONTENT_URI, null, null,
				new String[] { results, language, parserMode, orderBy }, " DESC");
		List<Movie> movies = setMovieList(cursor);

		if (orderBy.equals(MovieAppConstants.ORDER_BY_RELEASE)) {
			mWidgetMoviesByRelease = movies;
			Log.d(TAG, "in provideMovieListForWidget() setting mWidgetMoviesByRelease");
		} else if (orderBy.equals(MovieAppConstants.ORDER_BY_RATING)) {
			mWidgetMoviesByRating = movies;
			Log.d(TAG, "in provideMovieListForWidget() setting mWidgetMoviesByRating");
		} else {
			mWidgetMoviesByTitle = movies;
			Log.d(TAG, "in provideMovieListForWidget() setting mWidgetMoviesByTitle");
		}
	}

	public Movie provideMovieForWidget(final String orderBy, final int position) {
		Movie movie = null;
		if (orderBy.equals(MovieAppConstants.ORDER_BY_RELEASE)) {
			movie = mWidgetMoviesByRelease.get(position);
			Log.d(TAG,
					"orderBy = " + MovieAppConstants.ORDER_BY_RELEASE + " and movie for WIDGET is " + movie.toString());
		} else if (orderBy.equals(MovieAppConstants.ORDER_BY_RATING)) {
			movie = mWidgetMoviesByRating.get(position);
			Log.d(TAG, "orderBy = " + MovieAppConstants.ORDER_BY_RATING + " and movie for WIDGET is" + movie.toString());
		} else if (orderBy.equals(MovieAppConstants.ORDER_BY_TITLE)) {
			movie = mWidgetMoviesByTitle.get(position);
			Log.d(TAG, "orderBy = " + MovieAppConstants.ORDER_BY_TITLE + " and movie for WIDGET is" + movie.toString());
		}
		return movie;
	}

	public IFragmentCallback getMovieItems() {
		return mMovieItemsFragment;
	}

	public void setMovieItems(final IFragmentCallback movieItems) {
		this.mMovieItemsFragment = movieItems;
	}

	public IMovieInfoCallback getMovieInfoFragment() {
		return mMovieInfoFragment;
	}

	public void setMovieInfoFragment(final IMovieInfoCallback mMovieInfoFragment) {
		this.mMovieInfoFragment = mMovieInfoFragment;
	}

	public boolean isConnected() {
		return mIsConnected;
	}

	public void setIsConnected(final boolean mIsConnected) {
		this.mIsConnected = mIsConnected;
	}

	public void cancelMovieListFetching() {
		if (mMovieListRetrieverAsync != null) {
			Log.d(TAG, "canceling mMovieInfoScreensRetrieverAsync");
			mMovieListRetrieverAsync.cancel(true);
		}
	}

	public void cancelMovieDetailsFetching() {
		if (mMovieInfoRetrieverAsync != null) {
			Log.d(TAG, "canceling mMovieInfoRetrieverAsync");
			mMovieInfoRetrieverAsync.cancel(true);
		}
	}


	public void cancelScreensDownloading() {
		if (mMovieInfoScreensRetrieverAsync != null) {
			Log.d(TAG, "canceling mMovieInfoScreensRetrieverAsync");
			mMovieInfoScreensRetrieverAsync.cancel(true);
		}
	}

	public void cancelPosterDownloading() {
		if (mMoviePosterRetrieverAsync != null) {
			Log.d(TAG, "canceling mMoviePosterRetrieverAsync");
			mMoviePosterRetrieverAsync.cancel(true);
		}
	}

	/* builds movie list out of a cursor */
	private List<Movie> setMovieList(final Cursor cursor) {
		Log.d(TAG, "inside getMovieList() cursor.getCount() = " + cursor.getCount());

		if (cursor.moveToFirst()) {
			Log.d(TAG, "getting movies out of the cursor");
			List<Movie> movies = new ArrayList<Movie>();
			/* iterate over all cursor's rows and get column values into a movie object */
			do {
				Movie movie = new Movie();
				movie.setMovieId(cursor.getInt(cursor.getColumnIndex(MovieColumns.MOVIE_ID)));
				movie.setName(cursor.getString(cursor.getColumnIndex(MovieColumns.MOVIE_NAME)));
				movie.setCertification(cursor.getString(cursor.getColumnIndex(MovieColumns.MOVIE_CERTIFICATION)));
				movie.setVotes(cursor.getInt(cursor.getColumnIndex(MovieColumns.MOVIE_VOTES)));
				movie.setRating(cursor.getDouble(cursor.getColumnIndex(MovieColumns.MOVIE_RATING)));
				movie.setReleased(cursor.getString(cursor.getColumnIndex(MovieColumns.MOVIE_RELEASED)));
				movie.setUrl(cursor.getString(cursor.getColumnIndex(MovieColumns.MOVIE_URL)));
				movie.setThumbPosterUrl(cursor.getString(cursor.getColumnIndex(MovieColumns.MOVIE_POSTER_URL)));
				movies.add(movie);
			} while (cursor.moveToNext());
			cursor.close();
			return movies;
		}
		cursor.close();
		return null;
	}

	/* builds movie list out of a cursor */
	private void setMovieDetails(final MovieInfo movieInfo, final Cursor cursor) {
		Log.d(TAG, "inside setMovieDetails() cursor.getCount() = " + cursor.getCount());
		Log.d(TAG, "GETTING MOVIE INFO FROM cursor");
		if (cursor.moveToFirst()) {
			movieInfo.setMovieId(cursor.getInt(cursor.getColumnIndex(MovieColumns.MOVIE_ID)));
			movieInfo.setOverview(cursor.getString(cursor.getColumnIndex(MovieDetailsColumns.MOVIE_INFO_OVERVIEW)));
			movieInfo.setTrailerUrl(cursor.getString(cursor.getColumnIndex(MovieDetailsColumns.MOVIE_INFO_TRAILER)));
			movieInfo.setCastAsString(cursor.getString(cursor.getColumnIndex(MovieDetailsColumns.MOVIE_INFO_CAST)));

			cursor.close();
		}
		cursor.close();
	}

	private void setImageUrls(final MovieInfo movieInfo, final Cursor cursor) {
		Log.d(TAG, "inside setImageUrls() cursor.getCount() = " + cursor.getCount());
		Log.d(TAG, "GETTING MOVIE urls FROM cursor");
		movieInfo.setBackdropImagesUrls(new ArrayList<String>());
		movieInfo.setBackdropHDUrls(new ArrayList<String>());
		if (cursor.moveToFirst()) {
			do {
				String url = cursor.getString(cursor.getColumnIndex(MovieImageColumns.IMAGE_URL));
				if (cursor.getString(cursor.getColumnIndex(MovieImageColumns.IMAGE_SIZE)).equals("thumb")) {
					movieInfo.getBackdropImagesUrlsList().add(url);
				} else {
					movieInfo.getBackdropHDUrls().add(url);
				}
			} while (cursor.moveToNext());
		}
		cursor.close();
	}

	private synchronized MovieInfo getMovieScreen(final MovieInfo movieInfo) {

		List<String> backdropUrls = movieInfo.getBackdropImagesUrlsList();

		/* load screens from web and store them on a device */
		int backdropPosition = backdropUrls.size() - movieInfo.getBackdropBitmaps().size();
		Log.d(TAG, "backdrop urls = " + backdropUrls.size() + " backdrop bitmaps = "
				+ movieInfo.getBackdropBitmaps().size());
		if (backdropPosition > 0) {
			String url = backdropUrls.get(backdropPosition - 1);
			movieInfo.getBackdropBitmaps().add(mBitMapManager.loadBitmapFromLocalStorage(url));
			Log.d(TAG, "AFTER DOWNLOADING AND STORING 1 backdrop");
		}

		return movieInfo;
	}

	/* first asynchronous task that retrieves a movie list in a background thread */
	private class MovieListRetrieverAsync extends AsyncTask<Void, Void, List<Movie>> {

		private final String TAG = MovieListRetrieverAsync.class.getSimpleName();

		@Override
		protected void onPreExecute() {
			mMovieItemsFragment.onGettingMovieList();
		}

		@Override
		protected List<Movie> doInBackground(final Void... params) {
			/* get user settings */
			String language = mContext.getResources().getConfiguration().locale.getLanguage();
			Log.d(TAG, "LOCALE IS = " + language);

			String parserMode = mSettings.getParserMode();
			String orderBy = mSettings.getDefaultOrder();
			String results = mSettings.getMoviesDisplayed();

			/* get a list of movies */
			/* Cursor cursor =
			 * mContext.getContentResolver().query(ContentUris.withAppendedId(MovieListProvider.CONTENT_URI, 2), null,
			 * null, new String[] { results, language, parserMode }, " DESC"); */

			Cursor cursor = mContext.getContentResolver().query(MovieListProvider.CONTENT_URI, null, null,
					new String[] { language, parserMode, results, orderBy }, " DESC");
			Log.d(TAG, "GOT cursor");
			if (cursor == null) {
				Log.d(TAG, "cursor == null");
			} else {
				Log.d(TAG, "cursor != null");
				Log.d(TAG, "cursor.getColumnCount() = " + cursor.getColumnCount());
			}
			mMovies = setMovieList(cursor);

			return mMovies;
		}

		/* receives ArrayList<Movie> from doInBackground() */
		@Override
		protected void onPostExecute(final List<Movie> result) {
			if (result == null) {
				mMovieItemsFragment.onMovieListRetrievalFailed();
			} else {
				mMovieItemsFragment.onMovieListRetrieved(result);
			}
		}
	}

	/* second asynchronous task that retrieves a movie information in a background thread */
	private class MovieInfoRetrieverAsync extends AsyncTask<Integer, Void, MovieInfo> {

		private final String TAG = MovieInfoRetrieverAsync.class.getSimpleName();

		@Override
		protected void onPreExecute() {
			mMovieInfoFragment.onGettingMovieInfo();
		}

		@Override
		protected MovieInfo doInBackground(final Integer... params) {
			/* get user settings */
			String language = mContext.getResources().getConfiguration().locale.getLanguage();
			String parserMode = mSettings.getParserMode();

			int movieId = params[0];
			Log.d(TAG, "inside doInBackground(final Integer... params)\n and movieId = " + movieId);
			/* get a list of movies */
			Cursor movieInfoCursor = mContext.getContentResolver().query(
					ContentUris.withAppendedId(MovieDetailsProvider.CONTENT_URI, movieId), null, null,
					new String[] { language, parserMode }, "");
			mMovieInfo = new MovieInfo();
			setMovieDetails(mMovieInfo, movieInfoCursor);

			movieInfoCursor = mContext.getContentResolver().query(
					Uri.parse(MovieDetailsProvider.CONTENT_URI + "/backdrops/" + movieId), null, null,
					null, null);
			setImageUrls(mMovieInfo, movieInfoCursor);
			return mMovieInfo;
		}

		@Override
		protected void onPostExecute(final MovieInfo result) {
			if (result == null) {
				mMovieInfoFragment.onMovieInfoRetrievalFailed();
			} else {
				mMovieInfoFragment.onMovieInfoRetrieved(result);
			}
		}
	}

	/* third asynchronous task that searches for a movie title in a background thread */
	private class SearchMovieListAsync extends AsyncTask<String, Void, List<Movie>> {

		private final String TAG = SearchMovieListAsync.class.getSimpleName();

		@Override
		protected void onPreExecute() {
			mMovieItemsFragment.onGettingMovieList();
		}

		@Override
		protected List<Movie> doInBackground(final String... params) {
			/* get user settings */
			mLastSearchQuery = params[0];
			String language = mContext.getResources().getConfiguration().locale.getLanguage();
			String parserMode = mSettings.getParserMode();
			Log.d(TAG, "inside doInBackground(final String... params)\n and Search query IS " + mLastSearchQuery);

			Cursor cursor = mContext.getContentResolver().query(
					Uri.parse(MovieListProvider.CONTENT_URI + "/search/" + mLastSearchQuery), null, null,
					new String[] { language, parserMode }, " DESC");
			Log.d(TAG, "GOT cursor");
			if (cursor == null) {
				Log.d(TAG, "cursor == null");
			} else {
				Log.d(TAG, "cursor != null");
				Log.d(TAG, "cursor.getColumnCount() = " + cursor.getColumnCount());
			}
			mMoviesSearchList = setMovieList(cursor);
			return mMoviesSearchList;
		}

		/* receives ArrayList<Movie> from doInBackground() */
		@Override
		protected void onPostExecute(final List<Movie> result) {
			if (result.isEmpty()) {
				mMovieItemsFragment.onMovieListRetrievalFailed();
			} else {
				mMovieItemsFragment.onMovieListRetrieved(result);
			}
		}
	}

	/* fourth asynchronous task that refreshes the list of movies */
	private class RefreshMovieListAsync extends AsyncTask<Void, Void, List<Movie>> {

		private final String TAG = RefreshMovieListAsync.class.getSimpleName();

		@Override
		protected void onPreExecute() {
			mMovieItemsFragment.onGettingMovieList();
		}

		@Override
		protected List<Movie> doInBackground(final Void... params) {
			/* get user settings */
			String language = mContext.getResources().getConfiguration().locale.getLanguage();
			String parserMode = mSettings.getParserMode();
			String orderBy = mSettings.getDefaultOrder();
			String results = mSettings.getMoviesDisplayed();
			Log.d(TAG, "inside doInBackground(final Void... params)\n and REFRESHING ");

			/* get a list of movies */
			Cursor cursor = mContext.getContentResolver().query(MovieListProvider.CONTENT_URI, null, null,
					new String[] { language, parserMode, results, orderBy }, " DESC");
			Log.d(TAG, "GOT cursor");
			if (cursor == null) {
				Log.d(TAG, "cursor == null");
			} else {
				Log.d(TAG, "cursor != null");
				Log.d(TAG, "cursor.getColumnCount() = " + cursor.getColumnCount());
			}
			mMovies = setMovieList(cursor);

			return mMovies;
		}

		/* receives ArrayList<Movie> from doInBackground() */
		@Override
		protected void onPostExecute(final List<Movie> result) {
			if (result.isEmpty()) {
				mMovieItemsFragment.onMovieListRetrievalFailed();
			} else {
				mMovieItemsFragment.onMovieListRetrieved(result);
			}
		}
	}

	/* fifth asynchronous task that retrieves a movie screens in a background thread */
	private class MovieInfoScreensRetrieverAsync extends AsyncTask<MovieInfo, Void, MovieInfo> {

		private final String TAG = MovieInfoScreensRetrieverAsync.class.getSimpleName();

		@Override
		protected MovieInfo doInBackground(final MovieInfo... paramArrayOfParams) {
			return getMovieScreen(paramArrayOfParams[0]);
		}

		@Override
		protected void onPostExecute(final MovieInfo result) {
			Log.d(TAG, "onPostExecute(final MovieInfo result)");
			if (result == null) {
				mMovieInfoFragment.onMovieInfoRetrievalFailed();
			} else {
				mMovieInfoFragment.onMovieInfoRetrieved(result);
			}
		}
	}

	private class MoviePosterRetrieverAsync extends AsyncTask<ListWrapper, Bitmap, Void> {

		private final String TAG = MoviePosterRetrieverAsync.class.getSimpleName();

		@Override
		protected Void doInBackground(final ListWrapper... arrayOfUrls) {
			List<String> urls = arrayOfUrls[0].getPosterUrls();
			for (String url : urls) {
				publishProgress(mBitMapManager.loadBitmapFromLocalStorage(url));
			}
			return null;
		}

		@Override
		protected void onProgressUpdate(final Bitmap... values) {
			Log.d(TAG, "onProgressUpdate() publishing progress...");
			mMovieItemsFragment.onMoviePosterRetreived(values[0]);
		}
	}

	private class ListWrapper {

		private List<String> posterUrls;

		public List<String> getPosterUrls() {
			return posterUrls;
		}

		public void setPosterUrls(final List<String> posterUrls) {
			this.posterUrls = posterUrls;
		}
	}
}
